//
// Created by meow star on 2019/9/24.
//

#include "recall_types.h"
#include <fstream>
#define MAX_ALLOWED_ID 9999999

const string& ElemInfo::real_id() const{
    return _item_id;
}

/*
const string& ElemIdManager::get_key(uint32_t id) {
    if(id >= _vec_id_2_key.size()){
        return string("");
    }
    return _vec_id_2_key[id];
}

uint32_t ElemIdManager::alloc_id() {
    uint32_t idx = 0;
    if (! _st_spaces.empty())
    {
        idx = _st_spaces.front();
        _st_spaces.pop_front();
    }
    else{
        idx = _vec_id_2_key.size();
        _vec_id_2_key.push_back("");
    }
    return idx;
}

bool ElemIdManager::release_id(uint32_t id) {
    if(id >= _vec_id_2_key.size()){
        return false;
    }
    const string& item_id = get_key(id);
    auto it = _mp_key_2_id.find(item_id);
    if (it != _mp_key_2_id.end()){
        _mp_key_2_id.erase(it);
    }
    else{
        spdlog::warn("_mp_key_2_id not found while releasing!!!, id={}, itemid={}", id, item_id);
    }
    _vec_id_2_key[id] = "";
    _st_spaces.push_back(id);
    return true;
}

uint32_t ElemIdManager::get_id(string key) {
    auto it = _mp_key_2_id.find(key);
    if(it == _mp_key_2_id.end()){
        int id = alloc_id();
        _mp_key_2_id[key] = id;
        _vec_id_2_key[id] = key;
        if (id > MAX_ALLOWED_ID){
            spdlog::error(" id error, key={}, id={}", key, id);
        }
        return id;
    }
    else{
        if (it->second > MAX_ALLOWED_ID){
            spdlog::error(" id error, key={}, id={}", key, it->second);
        }
        return it->second;
    }
}

bool ElemIdManager::has(string key) {
    return  (_mp_key_2_id.find(key) != _mp_key_2_id.end());
}

int ElemIdManager::clear() {
    _mp_key_2_id.clear();
    _vec_id_2_key.clear();
    return 0;
}
*/
const ElemInfo* ForwardIndex::get_elem(const string &itemid) {
    auto it_elem = _mp_itemid_2_item.find(itemid);
    if (it_elem == _mp_itemid_2_item.end()){
        //spdlog::error("get_elem_by_lineno, itemid error, itemid={}", itemid);
        return NULL;
    }
    return it_elem->second;
}

const string& ForwardIndex::lineno_2_key(uint32_t lineno){
    auto& working_buffer = get_l2i_busy();
    auto it = working_buffer.find(lineno);
    if (it == working_buffer.end()){
        spdlog::error("get_elem_by_lineno, lineno error, lineno={}", lineno);
        return _default_string;
    }
    string& itemid = it->second;
    return itemid;
}

bool ForwardIndex::validate_loading_elem(uint32_t lineno, string& key){
    auto& idle = get_l2i_idle();
    auto it = idle.find(lineno);
    if (it == idle.end()){
        spdlog::error("validate_loading_elem, failed, lineno={}, key={}", lineno, key);
        return false;
    }
    return true;
}

const ElemInfo* ForwardIndex::get_elem(uint32_t lineno) {
    auto& working_buffer = get_l2i_busy();
    auto it = working_buffer.find(lineno);
    if (it == working_buffer.end()){
        spdlog::error("get_elem_by_lineno, lineno error, lineno={}", lineno);
        return NULL;
    }
    string& itemid = it->second;
    auto it1 = _mp_itemid_2_item.find(itemid);
    if (it1 == _mp_itemid_2_item.end()){
        spdlog::error("get_elem_by_lineno, itemid error, lineno={}, itemid={}", lineno, itemid);
        return NULL;
    }

    //因为更新和读取不在同一个线程内，所以如果没处理好，这里会core。
    //需要确保_mp_lineno_2_itemid双buffer切换完成后，才去回收_mp_itemid_2_item中的垃圾
    return it1->second;
}

int ForwardIndex::put_elem(string key, json* json_elem, const char* json_str, uint32_t lineno, uint32_t ts) {
    int ret = 0;
    auto it = _mp_itemid_2_item.find(key);
    ElemInfo* elem = NULL;
    if (it == _mp_itemid_2_item.end()){
        elem = new ElemInfo();
    }
    else{
        elem = it->second;
        if (elem == NULL){
            spdlog::error("put_elem, in-map elem=NULL!!!, lineno={}, itemid={}", lineno, key);
            elem = new ElemInfo();
        }
    }
    elem->last_update_ts = ts;
    elem->id = lineno;
    elem->set(key, json_elem, json_str);
    _mp_itemid_2_item[key] = elem;
    auto& idle = get_l2i_idle();
    idle[lineno] = key;
    return ret;
}

int ForwardIndex::clean_old_elem() {
    //这个函数需要在所有索引都switchbuffer后调用，不然会挂
    int recycled_count = 0;
    for(auto it = _mp_itemid_2_item.begin(); it != _mp_itemid_2_item.end(); ){
        ElemInfo* elem = it->second;
        if (elem == NULL){
            spdlog::info("clean_old_elem, elem=NULL!!!, itemid={}", it->first);
        }
        if (elem->real_id() != it->first){
            spdlog::info("clean_old_elem, itemid not match!!!, itemid={}, mapid={}", elem->real_id(), it->first);
        }
        if (elem->last_update_ts < _last_update){
            delete elem;
            _mp_itemid_2_item.erase(it ++ );
            recycled_count ++;
        }
        else{
            ++ it;
        }
    }
    spdlog::info("clean_old_elem, recycle_count={}", recycled_count);
    return 0;
}

int ForwardIndex::check_reload_object() {
    long last_modify = FileUtils::get_modify_time(_obj_path.data());
    uint32_t now = TimeUtils::now();
    if (now - last_modify > MODEL_RELOAD_DELAY && last_modify > _obj_timestamp){
        return 1;
    }
    return 0;
}

int ForwardIndex::reload_object() {
    ifstream json_file(_obj_path);
    if (json_file.is_open())
    {
        //这个时候才清除上一轮被switch下来的空闲缓存中的内容
        clean_old_elem();
        timeval ts;
        gettimeofday(&ts, NULL);
        uint32_t start = ts.tv_sec;
        const int READ_LINE_BUF_SIZE = 1024*10;
        char read_buf[READ_LINE_BUF_SIZE];
        int count = 0;
        int new_key = 0;
        while(json_file.getline(read_buf, READ_LINE_BUF_SIZE)){
            json j = json::parse(read_buf);
            string key = j[0].get<string>();
            if (key != ""){
                int ret = put_elem(key, &j, read_buf, count, start);
                if (ret == 1){
                    new_key ++;
                }
            }else{
                spdlog::error( "invalid record, key={}, record={}", key, j.dump());
            }
            count += 1;
            memset(read_buf, sizeof(char), READ_LINE_BUF_SIZE);
        }
        _last_update = start;
        gettimeofday(&ts, NULL);
        spdlog::error("done loading, count={}, new_key={}, forward_vec_size={}, cost={}s", count, new_key, _vec_elems.size(), ts.tv_sec - start);
        return 0;
    }
    else{
        return -1;
    }
}
